package com.hooda.session;

import com.hooda.pojo.Configuration;
import com.hooda.pojo.MappedStatement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.util.List;

/**
 * @author zhangzp
 * @version 1.0, 2021/7/29
 */
public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> E selectOne(String statementId, Object... parameters) throws Exception {
        List<Object> list = selectList(statementId, parameters);
        if (list.size() == 1) {
            return (E) list.get(0);
        }
        throw new RuntimeException("返回多条数据");
    }

    private  <E> E selectOne(MappedStatement mappedStatement, Object... parameters) throws Exception {
        List<Object> list = selectList(mappedStatement, parameters);
        if (list.size() == 1) {
            return (E) list.get(0);
        }
        throw new RuntimeException("返回多条数据");
    }

    @Override
    public <T> List<T> selectList(String statementId, Object... parameters) throws Exception {
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        return new SimpleExecutor().query(configuration, mappedStatement, parameters);
    }

    public <T> List<T> selectList(MappedStatement mappedStatement, Object... parameters) throws Exception {
        return new SimpleExecutor().query(configuration, mappedStatement, parameters);
    }

    public int insert(MappedStatement mappedStatement, Object... parameters) throws Exception {
        return new SimpleExecutor().insert(configuration, mappedStatement, parameters);
    }

    public int update(MappedStatement mappedStatement, Object... parameters) throws Exception {
        return new SimpleExecutor().update(configuration, mappedStatement, parameters);
    }

    public int delete(MappedStatement mappedStatement, Object... parameters) throws Exception {
        return new SimpleExecutor().delete(configuration, mappedStatement, parameters);
    }

    @Override
    public <T> T getMapper(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                String statementId = method.getDeclaringClass().getName() + "." + method.getName();
                MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                switch (mappedStatement.getType()) {
                    case insert:
                        return insert(mappedStatement, args);
                    case update:
                        return update(mappedStatement, args);
                    case delete:
                        return delete(mappedStatement, args);
                    case select:
                        if (method.getGenericReturnType() instanceof ParameterizedType) {
                            return selectList(mappedStatement, args);
                        }
                        return selectOne(mappedStatement, args);
                }

                throw new RuntimeException("undefined sql type");
            }
        });
    }
}
